﻿using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using SportsStore.WebUI.Controllers;
using System.Web.Mvc;
using SportsStore.WebUI.Models;

namespace SportsStore.UnitTests
{
    [TestClass]
    public class ProductControllerTest
    {
        [TestMethod]
        public void Can_Paginate()
        {
            // Arrange
            var products = new List<Product>()
            {
                new Product() { Name = "P1", ProductID = 1 },
                new Product() { Name = "P2", ProductID = 2 },
                new Product() { Name = "P3", ProductID = 3 },
                new Product() { Name = "P4", ProductID = 4 },
                new Product() { Name = "P5", ProductID = 5 },
            };

            var repository = new Mock<IProductRepository>();
            repository.Setup(repo => repo.Products).Returns(products.AsQueryable());

            var controller = new ProductController(repository.Object);
            controller.pageSize = 3;

            // Act
            var result = (ProductListViewModel)controller.List(null, 2).Model;

            //
            Assert.IsTrue(result.Products.Count() == 2, "There should only be 2 items in the list.");
            Assert.AreEqual(result.Products.First().Name, "P4", "The first item should be P1");
            Assert.AreEqual(result.Products.Last().Name, "P5", "Last item in the list should be P2");
        }

        [TestMethod]
        public void CanSend_PagingInfo_ToView()
        {
            // Arrange
            var products = new List<Product>()
            {
                new Product() { Name = "P1", ProductID = 1 },
                new Product() { Name = "P2", ProductID = 2 },
                new Product() { Name = "P3", ProductID = 3 },
                new Product() { Name = "P4", ProductID = 4 },
                new Product() { Name = "P5", ProductID = 5 },
            };

            var repository = new Mock<IProductRepository>();
            repository.Setup(repo => repo.Products).Returns(products.AsQueryable());

            var controller = new ProductController(repository.Object);
            controller.pageSize = 3;

            // Act
            var result = (ProductListViewModel)controller.List(null, 2).Model;

            // Assert
            Assert.AreEqual(2, result.PagingInfo.CurrentPage, "Should show the second page");
            Assert.AreEqual(3, result.PagingInfo.ItemsPerPage, "Should display only 4 items per page");
            Assert.AreEqual(5, result.PagingInfo.TotalItems, "Should only contain 5 items");
        }

        [TestMethod]
        public void Can_Filter_Products()
        {
            // Arrange
            var products = new List<Product>()
            {
                new Product() { Name = "P1", ProductID = 1, Category = "C1" },
                new Product() { Name = "P2", ProductID = 2, Category = "C2" },
                new Product() { Name = "P3", ProductID = 3, Category = "C2" },
                new Product() { Name = "P4", ProductID = 4, Category = "C2" },
                new Product() { Name = "P5", ProductID = 5, Category = "C1"},
            };

            var repository = new Mock<IProductRepository>();
            repository.Setup(repo => repo.Products).Returns(products.AsQueryable());

            var controller = new ProductController(repository.Object);
            controller.pageSize = 2;

            // Act
            var result = controller.List("C2", 1).Model as ProductListViewModel;

            // Assert
            Assert.AreEqual("C2", result.CurrentCategory, "Should use the expected category");
            Assert.IsTrue(result.Products.All(prod => prod.Category.Equals("C2")), "All the items should be of the same category");
            Assert.AreEqual(2, result.PagingInfo.ItemsPerPage, "Should pass the correct number of page size");
            Assert.AreEqual(3, result.PagingInfo.TotalItems, "Should calculate the number of items per category.");
            Assert.AreEqual(2, result.PagingInfo.TotalPages, "There should be two pages.");
            Assert.AreEqual(1, result.PagingInfo.CurrentPage, "The first page should be displayed");
            Assert.AreEqual(2, result.Products.First().ProductID, "The first product id should match.");
            Assert.AreEqual(3, result.Products.Last().ProductID, "The first product id should match.");
        }

        [TestMethod]
        public void GetImage_WhenTheItemCannotBeFound_ShouldReturnNull()    
        {
            // Arrange
            var repository = new Mock<IProductRepository>();
            repository.Setup(repo => repo.Products).Returns((new List<Product>()).AsQueryable());

            var controller = new ProductController(repository.Object);

            // Act
            var result = controller.GetImage(1);

            // Assert
            Assert.IsNull(result, "Should return null when the item cannot be found.");
        }

        [TestMethod]
        public void GetImage_WhenItemFound_ShouldReturnFromRepository()
        {
            // Arrange
            var products = new List<Product>()
            {
                new Product() { ProductID = 1, ImageData = new byte[10], ImageMimeType = "something" }
            };

            var repository = new Mock<IProductRepository>();
            repository.Setup(repo => repo.Products).Returns(products.AsQueryable());

            var controller = new ProductController(repository.Object);

            // Act
            var result = controller.GetImage(1);

            // Assert
            Assert.IsNotNull(result, "The item should be populated.");
            CollectionAssert.AreEqual(products.First().ImageData, result.FileContents, "The contents should match.");
        }
    }
}
